Published on

vue 基本概念

Authors
  • avatar
    Name
    jacob jiang

vuejs [2.X] 的理解

vue 为 js构建用户界面的库,通过编译模板生成【少部分用jsx】生成(最终都是调用createElement函数)虚拟dom ,通过改变虚拟dom【相当于给了一个缓冲层】, 来整体更新web 界面【将许多dom 操作在某个时刻来更新渲染,从而减少重绘,渲染的计算消耗】。

框架特点:响应式数据,数据变动产生副作用,模板语法【html 体验】

副作用包括:

  • data 数据改变,会去更新模板视图中的数据。例如:v-bind {{}}
  • compute 数据会根据data 【组件自生响应式状态】

模板语法

  • 声明式渲染,数据绑定
  • 条件渲染

💁‍♂️ v-show 不管如何都会调用render 函数生成虚拟节点,并且是基于css 。然而v-if 是惰性的【指不会去生成$el 对象】。

  • 列表渲染

💁‍♂️ v-if 与 v-for 一起使用不能放在同一个 标签上,看是为了渲染过滤后元素,还是为了隐藏列表。

​ 列表的key 是为了尽可能的最新代价更新。

  • 事件绑定
  • 数据双向绑定

指令: [参数].修饰符 = value

[参数] 为动态参数(使用变量代替)

vue object

创建一个vue 实例时,传入一个options 对象。 常见的选项:

image-20210720135602382

vue应用, 是许多vue实例以树形结构连接起来,与DOM 树类似。vue 应用挂载根元素(#app)实例,在mounted 阶段通过render 函数返回的createNodeDescription 的对象,进行渲染DOM节点。

createNodeDescription中的data数据 发生改变时,就会触发副作用 。触发更新阶段,会去比较新老节点对象,以最小代价更新dom 树。【例如:声明式绑定的数据的更新视图,其他computed 数据的变化】

template

🌟 注意 options 中往往有tempalte属性,.

而在项目中使用*.vue 单文件(Single-File Components)开发组件 template ,是由于vue-loader 的存在,结合webpack可以得到更好的开发体验,而不用写许多丑陋options 对象。

data

注意:data 中初始对象才是响应式的,而且被object.freeze对象无法被追踪变化。

⚠️ 多次复用组件中,data 需要写成一个函数形式【不能使用箭头函数,this 的指向不能确定】,相当一个工厂函数【用来生成装配不同的对象】。

箭头函数没有prototype ,因此没有 constructor,不能作为构造函数。

对于响应式数组的一般方法是被包裹,会触发视图更新。

Computed

该选项提供data 衍生出来的状态,例如 被某个属性筛选的列表的列表

⚠️ 计算属性默认只有get,可以提供set .

当需要在数据变化时执行异步或开销较大的操作时,watch这个声明式方式是最有用的。

methods

定义组件相关的事件,也可以理解为行为,当你点击或者拖拽某个组件,就会触发一些

状态更新,或传递给其他组件数据。这些都可以写进方法里。

组件相关选项

components

1️⃣ 组件全局注册 (注册基组件【常用的,例如btn,field】,也是各个组件库的注册方式)

Vue.component('my-component-name', { /* ... */ }) 组件名,选项

工厂函数方式加载组件

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 向 `resolve` 回调传递组件定义
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

2️⃣ 组件局部注册 (SFCs 常用的方式,即单文件通常导出一个对象,放入其父组件的Componets 选项中)

var ComponentA = { /* ... */ }
...

new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB,
		 'my-component': () => import('./my-async-component')//异步方式
  }
})

组件名参考风格指南 — Vue.js (vuejs.org)

Props

props: 字符串数组,对象与类型键值对,或者更高级,往往是父组件传给的数据(data),可是往往被用来初始组件的状态。

个人认为需要尽可能将皮肤(即css 能解决的问题),少写在props 中。使用工具类css 往往写出的组件更加简洁。

然而状态(state)或者 data 选项,表示组件内部的状态。一部分是通过props 初始的,另一部分是组件内部维护的状态。

复用性选项

mixins

数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。

同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。

值为对象的选项,例如 methodscomponentsdirectives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。

directives

directives: {//指令选项
  focus: {//指令名
    // 指令的定义(g钩子函数定义)
    inserted: function (el) {
      el.focus()
    }
  }
}

// 注册
Vue.directive('my-directive', {
  bind: function () {},
  inserted: function () {},
  update: function () {},
  componentUpdated: function () {},
  unbind: function () {}
})

钩子函数参数 el 渲染出来的dom ,可以直接操作

binding[指令相关所有值]

  • name , value ,arg ,modifiers ,expression
  • 当让可以使用动态arg

vnode 生成的虚拟节点

oldVnode 【只存在于update和componentUpdated】

可以使用过滤器格式化许多文本

动态组件

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

函数式组件

组件式函数,只是一个接受一些 prop 的函数。在这样的场景下,我们可以将组件标记为 functional

插槽

编译作用域

<navigation-link url="/profile"> 
  Clicking here will send you to: {{ url }}
  <!--
	url 作为props 传给该组件,里面可以访问插入slot 组件的作用域的data
  这里的 `url` 会是 undefined,因为其 (指该插槽的) 内容是
  _传递给_ <navigation-link> 的而不是
  在 <navigation-link> 组件*内部*定义的。
  -->
</navigation-link>

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

作用域插槽

为了让 user父级的插槽内容中可用,我们可以将 user 作为 <slot> 元素的一个 attribute 绑定上去:

<span>
  <slot v-bind:user="user"> //user attribute 被称为插槽 prop
    {{ user.lastName }}
  </slot>
</span>

然后在template 中改名

<current-user>
  <template v-slot:default="slotProps"> //slotProps 为所有插槽 props
  的对象
    {{ slotProps.user.firstName }}
  </template>
</current-user>

可以直接使用es6 结构 v-solt 给的 slotProps

<current-user v-slot="{ user: person }">
  {{ person.firstName }}
</current-user>

甚至使用 函数中的默认值,用法

<current-user v-slot="{ user = { firstName: 'Guest' } }">
  {{ user.firstName }}
</current-user

也可以使用动态插槽名

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

生命周期

注意几个重要节点

  • beforeCreate ,在data observer ,injection 初始前,在event与lifeCycle初始化后被调用。

  • created ,此时 data 可以被拿到,可以做些副作用【网络请求】

    然后:

    通过两种方式去挂载,一种是当含有el 参数,反之是当有vm.$mount 调用时(手动)。

    会去编译Template :

    • 如果有template则变为render 函数
    • 反之将outerHtml 编译为template [ 需要compiler-included build 版本],然而要渲染最终还是要编译为render函数。一般不走这条

    服务端渲染时期被调用


    此时开始渲染在页面上

  • beforeMount ,render 函数首次被调用,返回虚拟dom --- creating vm.$el and to replace 'el' with it 。在创建文档流。还未挂载不会触发update。

  • mouted ,可以拿到elel 与refs。 mouted 并不会保证所有的子组件都会重绘完成。要使用$nextTick()

💁‍♂️ 与updated 一样不能保证子组件的渲染完成,可以使用$nextTick

mounted: function () {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
  })
}

lifecycle.png

  • 当(data)数据更新时会去触发更新阶段,beforeUpate 【手动移除事件监听器】 -->re-render / patch --> updated。

    beforeUpdate 可以对数据进行更改,并不会触发updated

    update此时可以对新节点进行dom 操作。然而在大多数情况下,你应该避免在此期间更改状态【否则又会触发更新!】。如果要相应状态改变,通常最好使用计算属性watcher 取而代之。

  • beforeDestroy 实例销毁前最后一次hook,可以销毁一些引入的对象[listener, 定时器]

⚠️ 不要再生命周期上使用箭头函数 与 option property

api

实例property

vue内置属性以开头,不常用的但重要,如开头,不常用的但重要,如attrs,listeners,listeners,slots,$scopedSlots 等

Vue.observable( object )

watch

el

parent

extends

provide / inject

祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。

model

虚拟dom

Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。

return createElement('h1', this.blogTitle)

返回不是一个实际的 DOM 元素。它更准确的名字可能是 createNodeDescription

深入数据对象

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一个 HTML 标签名、组件选项对象,或者
  // resolve 了上述任何一种的一个 async 函数。必填项。
  'div',

  // {Object}
  // 一个与模板中 attribute 对应的数据对象。可选。
  {
    // (详情见深入数据对象节)
  },

  // {String | Array}
  // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
  // 也可以使用字符串来生成“文本虚拟节点”。可选。
  [
    '先写一些文字',
    createElement('h1', '一则头条'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

Jsx 语法